package cn.calm.xhtml.impl;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.dom4j.tree.DefaultText;

import cn.calm.xhtml.api.NodeProcess;
import cn.calm.xhtml.api.Template;
import cn.calm.xhtml.api.TemplateEngine;
import cn.calm.xhtml.api.exception.XhtmlException;
import cn.calm.xhtml.api.service.XhtmlContext;
import cn.calm.xhtml.impl.html.Default;
import cn.calm.xhtml.impl.ui.TextNode;

public class DefaultTemplate implements Template {
	private TemplateEngine engine;
	private URL url;
	private static final Map<URL, NodeProcess> cache = new HashMap<>();

	public DefaultTemplate(TemplateEngine engine, URL url) {
		this.setEngine(engine);
		this.url = url;
	}

	@Override
	public void render(XhtmlContext context) {
		NodeProcess node = cache.get(url);
		// NodeProcess node = null;
		if (node == null) {
			SAXReader reader = new SAXReader();
			Document document;
			try {
				document = reader.read(url);
				Element rootElement = document.getRootElement();
				node = parseNode(rootElement, context, null);
//				cache.put(url, node);
				// ByteArrayOutputStream baos = null;
				// ObjectOutputStream oos = null;
				// try{
				// baos=new ByteArrayOutputStream();
				// oos=new ObjectOutputStream(baos);
				// oos.writeObject(node);
				// oos.flush();
				// bs = baos.toByteArray();
				// cache.put(url, bs);
				// } catch (IOException e) {
				// throw new XhtmlException(e);
				// }finally{
				// if(oos!=null){
				// try {
				// oos.close();
				// } catch (IOException e) {
				// e.printStackTrace();
				// }
				// }
				// if(baos!=null){
				// try {
				// baos.close();
				// } catch (IOException e) {
				// e.printStackTrace();
				// }
				// }
				// }
			} catch (DocumentException e) {
				throw new XhtmlException(e);
			} catch (InstantiationException e) {
				throw new XhtmlException(e);
			} catch (IllegalAccessException e) {
				throw new XhtmlException(e);
			}
		}
		// else{
		// ByteArrayInputStream bais = null;
		// ObjectInputStream ois = null;
		// try {
		// bais=new ByteArrayInputStream(bs);
		// ois=new ObjectInputStream(bais);
		// node=(NodeProcess) ois.readObject();
		// } catch (IOException | ClassNotFoundException e) {
		// throw new XhtmlException(e);
		// }finally{
		// if(ois!=null){
		// try {
		// ois.close();
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
		// }
		// if(bais!=null){
		// try {
		// bais.close();
		// } catch (IOException e) {
		// e.printStackTrace();
		// }
		// }
		// }

		// }
		node.processNode(context);
		try {
			context.getResponseWriter().getWriter().flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	private NodeProcess parseNode(Element element, XhtmlContext xhtmlContext,
			NodeProcess parent) throws InstantiationException,
			IllegalAccessException {

		String name = element.getName();
		String namespaceURI = element.getNamespaceURI();
		NodeProcess nodeProcess;
		if (namespaceURI.equals("http://www.w3.org/1999/xhtml")) {
			nodeProcess = new Default();
		} else {
			Map<String, Class<NodeProcess>> map2 = engine.getHandlers().get(
					namespaceURI);
			if (map2 == null) {
				throw new XhtmlException(namespaceURI + " is not found");
			}
			Class<NodeProcess> class1 = map2.get(name.toLowerCase());
			if (class1 == null) {
				throw new XhtmlException(namespaceURI + ":" + name
						+ " is not found");
			}
			nodeProcess = class1.newInstance();
		}

		nodeProcess.setName(name);
		nodeProcess.setParent(parent);
		String text = element.getText();
		nodeProcess.setContent(text);

		List<Attribute> attributes = element.attributes();

		for (Attribute at : attributes) {
			String value2 = at.getValue();
			String name2 = at.getName();
			nodeProcess.setAttr(name2, value2);
		}
		List<NodeProcess> nodeProcesses = new ArrayList<>();
		List<Node> elements = element.content();
		for (Node e : elements) {
			if (e instanceof Element) {
				nodeProcesses.add(parseNode((Element) e, xhtmlContext,
						nodeProcess));
			} else if (e instanceof DefaultText) {
				TextNode textNodeProcess = new TextNode();
				textNodeProcess.setName("");
				textNodeProcess.setContent(e.getText());
				textNodeProcess.setParent(nodeProcess);
				nodeProcesses.add(textNodeProcess);
			}
		}
		NodeProcess[] children = new NodeProcess[nodeProcesses.size()];
		nodeProcesses.toArray(children);
		nodeProcess.setChildren(children);
		return nodeProcess;
	}

	public TemplateEngine getEngine() {
		return engine;
	}

	public void setEngine(TemplateEngine engine) {
		this.engine = engine;
	}

}
